# import pandas as pd
# import torch
# import torch.nn as nn
# import streamlit as st
# # 数据读取和预处理
# data=pd.read_csv("./ir.csv")
# data1=data.drop(data[:,0])
# data2=torch.tensor(data1)
# x=data2[:,0:3]
# y=data2[:,4]
# label_name={
#     0:"setosa",
#     1:"versicolor",
#     2:"virginica"
# }
# # 与streamlit交互
# st.title("鸢尾花多分类系统")
# st.slider('萼长',0,8,4)
# st.slider('萼宽',0,5,2.5)
# st.slider('瓣长',0,7,3.5)
# st.slider('瓣宽',0,3,1.5)
#
# # 创建模型
# model=nn.Sequential(
#     nn.Flatten(),
#     nn.Linear(16*16, 64),
#     nn.ReLU(),
#     nn.Linear(64, 128),
#     nn.ReLU(),
#     nn.Linear(128, 64),
#     nn.ReLU(),
#     nn.Linear(64, 3),
# )
# # 3. 损失函数
# loss_fn = nn.CrossEntropyLoss()
#
# # 4. 优化器
# optimizer = torch.optim.SGD(model.parameters(), lr=lr)
#
# # 5. 循环训练
# lr=0.01
# Epochs=1000
# model.train()
# for epoch in range(Epochs):
#     # 前向传播
#     y_pre = model(x)
#     # 计算损失
#     loss = loss_fn(y_pre, y)
#     # 清空梯度
#     optimizer.zero_grad()
#     # 反向传播
#     loss.backward()
#     # 更新参数
#     optimizer.step()
#
#     # 打印训练信息
#     if epoch == 0 or (epoch + 1) % 100 == 0:
#         print(f"[{epoch + 1}/{Epochs}] Loss:{loss.item():.4f}")
# # 保存模型
# torch.save(model, "./model.pth")
# # 加载已经保存的训练好的模型
# model = torch.load("./model.pth", weights_only=False)
# y_pre = model(x)
# print(y_pre)
# # 读取最大值索引，通过该索引下标去读取对应的标签名
# idx = y_pre.argmax(dim=-1)
# print(idx)
#
# for i in idx:
#     print(label_name[i.item()])

import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
import streamlit as st
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score

# 加载鸢尾花数据集
iris = load_iris()
X = iris.data
y = iris.target
feature_names = iris.feature_names
target_names = iris.target_names

# 数据标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 转换为PyTorch张量
X_tensor = torch.FloatTensor(X_scaled)
y_tensor = torch.LongTensor(y)

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X_tensor, y_tensor, test_size=0.2, random_state=42)

# 标签映射
label_name = {
    0: "setosa",
    1: "versicolor",
    2: "virginica"
}

# 创建模型 - 修正输入维度为4（4个特征）
model = nn.Sequential(
    nn.Linear(4, 64),  # 输入层：4个特征
    nn.ReLU(),
    nn.Linear(64, 128),
    nn.ReLU(),
    nn.Linear(128, 64),
    nn.ReLU(),
    nn.Linear(64, 3),  # 输出层：3个类别
)

# 损失函数和优化器
loss_fn = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

# Streamlit页面设计
st.title("🌸 鸢尾花多分类系统")
st.markdown("基于神经网络的鸢尾花种类预测")

# 侧边栏 - 模型训练
with st.sidebar:
    st.header("模型训练设置")
    epochs = st.slider("训练轮次", 100, 5000, 1000, 100)

    if st.button("开始训练模型"):
        # 训练模型
        model.train()
        losses = []

        progress_bar = st.progress(0)
        status_text = st.empty()

        for epoch in range(epochs):
            # 前向传播
            y_pred = model(X_train)

            # 计算损失
            loss = loss_fn(y_pred, y_train)

            # 反向传播
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            losses.append(loss.item())

            # 更新进度
            if (epoch + 1) % 100 == 0:
                progress = (epoch + 1) / epochs
                progress_bar.progress(progress)
                status_text.text(f"训练中... {epoch + 1}/{epochs} 轮次")

        # 评估模型
        model.eval()
        with torch.no_grad():
            test_pred = model(X_test)
            test_pred_classes = torch.argmax(test_pred, dim=1)
            accuracy = accuracy_score(y_test.numpy(), test_pred_classes.numpy())

        st.success(f"训练完成！测试集准确率: {accuracy:.2%}")
        st.session_state['trained_model'] = model
        st.session_state['training_complete'] = True

# 主内容区
tab1, tab2, tab3 = st.tabs(["数据概览", "特征输入", "预测结果"])

with tab1:
    st.header("鸢尾花数据集概览")

    # 显示数据集信息
    col1, col2, col3 = st.columns(3)
    with col1:
        st.metric("样本数量", len(X))
    with col2:
        st.metric("特征数量", len(feature_names))
    with col3:
        st.metric("类别数量", len(target_names))

    # 显示特征名称
    st.subheader("特征名称")
    for i, feature in enumerate(feature_names):
        st.write(f"{i + 1}. {feature}")

    # 显示类别名称
    st.subheader("类别名称")
    for i, name in enumerate(target_names):
        st.write(f"{i}. {name}")

    # 显示数据样本
    st.subheader("数据样本（前10行）")
    df = pd.DataFrame(X, columns=feature_names)
    df['target'] = y
    df['species'] = df['target'].map(label_name)
    st.dataframe(df.head(10))

with tab2:
    st.header("输入鸢尾花特征")

    col1, col2 = st.columns(2)

    with col1:
        sepal_length = st.slider('萼片长度 (cm)', 4.0, 8.0, 5.4, 0.1)
        sepal_width = st.slider('萼片宽度 (cm)', 2.0, 5.0, 3.4, 0.1)

    with col2:
        petal_length = st.slider('花瓣长度 (cm)', 1.0, 7.0, 4.3, 0.1)
        petal_width = st.slider('花瓣宽度 (cm)', 0.1, 2.5, 1.3, 0.1)

    # 显示输入的特征值
    st.subheader("输入的特征值")
    input_features = {
        '萼片长度': sepal_length,
        '萼片宽度': sepal_width,
        '花瓣长度': petal_length,
        '花瓣宽度': petal_width
    }
    st.json(input_features)

with tab3:
    st.header("预测结果")

    if 'training_complete' not in st.session_state:
        st.info("请先在侧边栏训练模型")
    else:
        if st.button("进行预测"):
            # 准备输入数据
            input_data = np.array([[sepal_length, sepal_width, petal_length, petal_width]])
            input_scaled = scaler.transform(input_data)
            input_tensor = torch.FloatTensor(input_scaled)

            # 进行预测
            model = st.session_state['trained_model']
            model.eval()

            with torch.no_grad():
                output = model(input_tensor)
                probabilities = torch.softmax(output, dim=1).numpy()[0]
                predicted_class = torch.argmax(output, dim=1).item()

            # 显示预测结果
            st.success(f"预测结果: {label_name[predicted_class]}")

            # 显示概率分布
            st.subheader("各类别概率")
            col1, col2, col3 = st.columns(3)

            with col1:
                st.metric("setosa", f"{probabilities[0]:.2%}")
                st.progress(float(probabilities[0]))

            with col2:
                st.metric("versicolor", f"{probabilities[1]:.2%}")
                st.progress(float(probabilities[1]))

            with col3:
                st.metric("virginica", f"{probabilities[2]:.2%}")
                st.progress(float(probabilities[2]))

            # 显示详细的预测信息
            st.subheader("预测详情")
            result_df = pd.DataFrame({
                '类别': list(label_name.values()),
                '概率': [f"{p:.2%}" for p in probabilities],
                '原始分数': [f"{s:.4f}" for s in output.numpy()[0]]
            })
            st.dataframe(result_df)

            # 高亮显示预测结果
            st.markdown(f"""
            <div style="background-color: #e8f5e8; padding: 20px; border-radius: 10px; text-align: center;">
                <h3>🎯 最终预测: {label_name[predicted_class]}</h3>
                <p>置信度: {probabilities[predicted_class]:.2%}</p>
            </div>
            """, unsafe_allow_html=True)

# 添加一些样式
st.markdown("""
<style>
    .stProgress > div > div > div > div {
        background-color: #4CAF50;
    }
    .metric-container {
        background-color: #f0f2f6;
        padding: 10px;
        border-radius: 10px;
        margin: 5px 0;
    }
</style>
""", unsafe_allow_html=True)

